# Copyright (c) 2019 Presto Labs Pte. Ltd.
# Author: xguo

import enum
import functools

from coin.exchange.base.kr_rest.private_client_base import PrivateClientBase
from coin.exchange.bitstamp_v2.kr_rest.product import (
    BitstampProduct,
    BitstampCurrency,
)
from coin.proto.coin_query_pb2 import (
    AccountBalance,
    CurrencyBalance,
)
from coin.exchange.bitstamp_v2.kr_rest.native_private_client import (
    BitstampNativePrivateClient,)
from coin.exchange.bitstamp_v2.kr_rest.constants import native_currency_list


class BitstampBalanceType(enum.Enum):
  BALANCE = 'balance'
  RESERVED = 'reserved'
  AVAILABLE = 'available'


@functools.lru_cache(maxsize=64)
def balance_type_to_proto_field(balance_type: BitstampBalanceType):
  value_map = {
      'balance': 'total',
      'reserved': 'hold',
      'available': 'available',
  }
  return value_map[balance_type.value]


@functools.lru_cache(maxsize=64)
def parse_balance_field_name(balance_field_name):
  native_currency, balance_type = balance_field_name.split('_')
  assert native_currency in native_currency_list, native_currency
  currency = BitstampCurrency.FromStrNativeCurrencyNothrow(native_currency)
  return currency, BitstampBalanceType(balance_type)


class BitstampPrivateParser(object):
  @staticmethod
  def parse_account_balance(msg):
    """
    {
      "bch_available": "0.00000000",
      "bch_balance": "0.00000000",
      "bch_reserved": "0.00000000",
      "bchbtc_fee": "0.25",
      "bcheur_fee": "0.25",
      "bchusd_fee": "0.25",
      "btc_available": "0.00000000",
      "btc_balance": "0.00000000",
      "btc_reserved": "0.00000000",
      "btceur_fee": "0.25",
      "btcusd_fee": "0.25",
      "eth_available": "0.00000000",
      "eth_balance": "0.00000000",
      "eth_reserved": "0.00000000",
      "ethbtc_fee": "0.25",
      "etheur_fee": "0.25",
      "ethusd_fee": "0.25",
      "eur_available": "0.00",
      "eur_balance": "0.00",
      "eur_reserved": "0.00",
      "eurusd_fee": "0.25",
      "ltc_available": "0.00000000",
      "ltc_balance": "0.00000000",
      "ltc_reserved": "0.00000000",
      "ltcbtc_fee": "0.25",
      "ltceur_fee": "0.25",
      "ltcusd_fee": "0.25",
      "usd_available": "0.00",
      "usd_balance": "0.00",
      "usd_reserved": "0.00",
      "xrp_available": "0.00000000",
      "xrp_balance": "0.00000000",
      "xrp_reserved": "0.00000000",
      "xrpbtc_fee": "0.25",
      "xrpeur_fee": "0.25",
      "xrpusd_fee": "0.25"
    }
    """
    balance_dict = {}
    for key, value in msg.items():
      if 'fee' in key:
        continue  # Simply ignore
      if float(value) == 0:
        continue

      currency, balance_type = parse_balance_field_name(key)
      if currency in balance_dict:
        balance = balance_dict[currency]
      else:
        balance = CurrencyBalance(currency=currency.currency,
                                  currency_native=currency.native_currency)
        balance_dict[currency] = balance

      attr = balance_type_to_proto_field(balance_type)
      setattr(balance, attr, float(value))
    return AccountBalance(
        exchange='Bitstamp',
        market_type='Spot',
        each_balance=balance_dict.values(),
    )


class BitstampPrivateClient(PrivateClientBase):
  ProductType = BitstampProduct

  def __init__(self, key_file):
    super().__init__()
    self.nprivc = BitstampNativePrivateClient(key_file=key_file)

  def query_account_balance_impl(self):
    update = self.nprivc.query_account_balance()
    update.msg = BitstampPrivateParser.parse_account_balance(update.msg)
    return update

  def query_account_position_impl(self):
    raise NotImplementedError()

  def query_list_orders_impl(self, product):
    raise NotImplementedError()

  def query_fills_impl(self, product):
    raise NotImplementedError()

  def query_submit_impl(self, product, side, price, qty):
    raise NotImplementedError()

  def query_cancel_impl(self, product, order_id):
    raise NotImplementedError()

  def query_cancel_product_impl(self, product):
    raise NotImplementedError()

  def query_account_fills_impl(self):
    raise NotImplementedError()

  def query_account_list_orders_impl(self):
    raise NotImplementedError()
